<?php
// +----------------------------------------------------------------------
// | 基础类型服务层
// +----------------------------------------------------------------------
// | 主要服务于任务相关数据操作
// +----------------------------------------------------------------------
// | 错误编码头 201xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\OrderModel;
use common\model\OrderTaskListModel;
use common\model\ProjectMemberModel;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\ProjectTemplateModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\TaskExecutorRecordModel;
use common\model\TaskModel;
use common\model\TimelogModel;
use common\model\UserModel;
use support\EntityTaskFilterParser;
use support\SingletonTrait;
use think\Exception;
use support\ErrorCode;
use think\Hook;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;
use function request;


class TaskAssignService
{

    /** @var TaskModel */
    private $taskModel;
    private $entityModel;

    use EntityTaskFilterParser;

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->taskModel = model(TaskModel::class);
        $this->entityModel = model(EntityModel::class);
    }


    /**
     * 查询可分配的供应商树
     * @param $param
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function getCanAssignSupplierTaskTree($param, $tenantId)
    {
        $taskIdList = $param['filter']['task_id'];
        $episodeId = $param['filter']['episode_id'];

        $episode = model(EntityModel::class)->find($episodeId);
        $taskList = $this->taskModel->where(['id' => ['IN', implode(',', $taskIdList)]])
            ->field("id,name,code,entity_id,plan_start_time as start_time,plan_end_time as end_time,is_assignee,assignee_producer,assignee_at,executor_tenant_id,assignee,step_type,step_id,step_category_id,IF(executor_tenant_id <> tenant_id and executor_tenant_id <> 0, 'yes', 'no') as is_outside,assign_status")
            ->select();
        $stepCategoryIdList = array_column($taskList, 'step_category_id', 'step_category_id');
        $entityIdList = array_column($taskList, 'entity_id');
        $entityIdList = array_unique($entityIdList);
        /**
         * 查询供应商信息
         * 然后循环筛选出可以执行任务的人员
         * 内部
         * 外部 企业认证必须完成
         */
        $param = [
            'param' => [
                'filter' => [
                    [
                        [
                            'supplier.type' => 'outside',
                            'framework_agreement_status' => 'signed',
                            'tenant_authentication.status' => 'active',
                            'tenant_authentication.fdd_cert_status' => '4'
                        ],
                        'supplier.type' => 'inside',
                        '_logic' => '-or'
                    ],
                    'supplier.tenant_id' => $tenantId,
                ],
            ]
        ];
        $supplierList = teamones_request('teamones-im', 'supplier/select', $param)['data'];
        if (empty($supplierList['rows'])) {
            throw new LogicException("There is no supplier available", ErrorCode::THERE_IS_NO_SUPPLIER_AVAILABLE);
        }
        $insideUserIdList = array_column($supplierList['rows'], 'user_id');
        $userService = UserService::getInstance();
        $userData = $userService->getUsersByUnionIds($insideUserIdList, 'id,union_id');
        if (empty($userData)) {
            throw new LogicException("user info not found", ErrorCode::USER_INFO_NOT_FOUND);
        }
        $userData = array_column($userData, 'id', 'union_id');
        $entityMap = model(EntityModel::class)
            ->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,name,code,module_id,json')
            ->select();
        $entityMap = array_column($entityMap, null, 'id');
        $moduleIdList = array_column($entityMap, 'module_id');
        $moduleIdList = array_unique($moduleIdList);
        $stepCategoryList = model(StepCategoryModel::class)
            ->where([
                'entity_id' => ['IN', implode(',', $moduleIdList)],
                'parent_id' => 0,
            ])->field('name,entity_id')->select();
        $stepCategoryList = array_column($stepCategoryList, 'name', 'entity_id');
        foreach ($entityMap as &$entity) {
            $entity['step_category_name'] = $stepCategoryList[$entity['module_id']] ?? '';
            $entity = array_merge($entity, json_decode($entity['json'], true));
            unset($entity['json']);
        }
        $planService = PlanService::getInstance();
        // 集计划
        $episodePlan = $planService->getPlan(['entity_id' => $episodeId, 'type' => 'episode']);
        // 集分类计划
        $stepCategoryPlan = model(PlanModel::class)
            ->join("step_category as sc ON sc.id = plan.step_category_id")
            ->where(['plan.entity_id' => $episodeId, 'plan.step_category_id' => ['IN', implode(',', $stepCategoryIdList)], 'plan.type' => 'step_category'])
            ->field('plan.*,sc.name as step_category_name')
            ->select();
        $stepCategoryPlan = array_column($stepCategoryPlan, null, 'step_category_id');
        $timeRanges = [];
        // 组装 timerange
        foreach ($stepCategoryPlan as $plan) {
            $timeRanges[] = [
                "name" => $episode['name'] . $plan['step_category_name'] . "计划结束日期",
                "startDate" => date("Y-m-d", $plan['end_time']),
                "duration" => 0,
                "durationUnit" => "d",
                "cls" => "b-fa b-fa-diamond",
            ];
        }
        $entityIdList = array_column($taskList, 'entity_id', 'entity_id');
        $entityPlans = $planService->getPlans(['entity_id' => ['in', implode(',', $entityIdList)], 'type' => 'entity']);
        $entityPlans = array_column($entityPlans, null, 'entity_id');

        $entityIdList = array_column($taskList, 'entity_id', 'entity_id');
        $taskStepCategoryIdList = array_column($taskList, 'step_category_id', 'step_category_id');
        $entityStepCategoryPlans = $planService->getPlans(['entity_id' => ['in', implode(',', $entityIdList)], 'step_category_id' => ['IN', implode(',', $taskStepCategoryIdList)], 'type' => 'entity_step_category']);
        $entityStepCategoryPlansGrouped = array_group_by($entityStepCategoryPlans, 'entity_id');
        foreach ($entityStepCategoryPlansGrouped as &$entityStepCategoryPlans) {
            $entityStepCategoryPlans = array_column($entityStepCategoryPlans, null, 'step_category_id');
        }
        // 填充计划限制
        foreach ($taskList as &$task) {
            $currentPlan = $entityStepCategoryPlansGrouped[$task['entity_id']][$task['step_category_id']] ?? null;
            if (empty($currentPlan)) {
                $currentPlan = $entityPlans[$task['entity_id']] ?? null;
                if (empty($currentPlan)) {
                    $currentPlan = $stepCategoryPlan[$task['step_category_id']] ?? ['start_time' => null, 'end_time' => null];
                }
            }
            $task['plan_start_time'] = $currentPlan['start_time'];
            $task['plan_end_time'] = $currentPlan['end_time'];
            $task['expanded'] = false;
            $task['real_id'] = $task['id'];
            $task['id'] = 'task_' . $task['id'];
            StepService::formatGanttItem($task);
        }
        $index = 0;
        foreach ($supplierList['rows'] as $supplierKey => &$supplier) {
            if ($supplier['type'] === 'inside') {
                $departmentName = $supplier['department_name'] ?? '未知部门';
                $supplier['name'] = $departmentName . '-' . $supplier['user_name'];
            } else {
                $supplier['name'] = $supplier['from_tenant_name'];
            }
            $userId = $userData[$supplier['user_id']] ?? 0;
            $supplierTenantId = $supplier['from_tenant_id'];
            if ($userId === 0) {
                unset($supplierList['rows'][$supplierKey]);
                continue;
            }
            $diff = array_diff($stepCategoryIdList, explode(',', $supplier['step_category_ids']));
            if (count($diff) !== 0) {
                unset($supplierList['rows'][$supplierKey]);
                continue;
            }
            if ($supplier['type'] === 'inside') {
                // 筛选出他的任务
                $supplier['children'] = array_filter($taskList, function ($item) use ($userId) {
                    return $item['assignee'] == $userId;
                });
            } else {
                $supplier['children'] = array_filter($taskList, function ($item) use ($supplierTenantId) {
                    return $item['executor_tenant_id'] == $supplierTenantId;
                });
            }
            $supplier['real_id'] = $supplier['id'];
            $supplier['id'] = 'supplier_' . $supplier['id'];
            StepService::formatGanttItem($supplier);
            $childrenTree = [];
            if (!empty($supplier['children'])) {
                $grouped = array_group_by($supplier['children'], 'entity_id');
                foreach ($grouped as $groupKey => $tempTaskList) {
                    if (!isset($childrenTree[$groupKey])) {
                        $tmpEntity = $entityMap[$groupKey] ?? [];
                        if (empty($tmpEntity)) {
                            continue;
                        }
                        $index += 1;
                        $tmpEntity['real_id'] = $tmpEntity['id'];
                        $tmpEntity['id'] = 'entity_' . $tmpEntity['id'] . '-' . $index;
                        $tmpEntity['expanded'] = false;
                        StepService::formatGanttItem($tmpEntity);
                        $childrenTree[$groupKey] = $tmpEntity;
                    }
                    $childrenTree[$groupKey]['children'] = $tempTaskList;
                }
                $supplier['children'] = array_values($childrenTree);
            }
        }
        $supplierList['rows'] = array_sort_by($supplierList['rows'], 'type');
        return StepService::formatStepCategoryPlanData(array_values($supplierList['rows']), $episodePlan, [], $timeRanges);
    }

    /**
     * 分配任务给供应商
     * @param $dataList
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws Exception
     * @throws \Exception
     */
    public function assignTaskToSupplierBatch($dataList, $userId, $tenantId)
    {
        $commonService = CommonService::instance('task');
        $commonService->setCurrentModuleCode('task');
        $res = [];
        $userService = UserService::getInstance();
        foreach ($dataList as $data) {
            $user = $userService->getUserByUnionId($data['user_id']);
            if (empty($user)) {
                $user['id'] = 0;
            }
            $updateData = [
                'id' => $data['task_id'],
                'assign_at' => time(),
                'assignee_producer' => $userId,
            ];
            $data['supplier_type'] = $data['supplier_type'] ?? "outside";
            $updateData['executor_type'] = $data['supplier_type'];
            if ($data['supplier_type'] === 'outside') {
                $updateData['executor_tenant_id'] = $data['tenant_id'];
                $updateData['executor'] = $user['id'];
                $updateData['assignee'] = 0;
                $updateData['assign_status'] = 'default';
                $updateData['department_id'] = 0;
            } elseif ($data['supplier_type'] === 'tenant_group') {
                $updateData['executor_tenant_id'] = $data['tenant_id'];
                $updateData['executor'] = $user['id'];
                $updateData['assignee'] = 0;
                $updateData['assign_status'] = 'default';
                $updateData['department_id'] = 0;

                if (!empty($data["tenant_group_id"])) {
                    $updateData["tenant_group_id"] = $data["tenant_group_id"];
                } else {
                    throw new LogicException("tenant_group_id not exist", ErrorCode::DATA_INVALID);
                }
            } else {
                $updateData['assignee'] = $user['id'];
                $updateData['executor_tenant_id'] = 0;
                $updateData['assign_status'] = 'wait_confirm';
                $updateData['executor'] = 0;
            }

            $res[] = $commonService->update($updateData, 'task', false);
        }

        return $res;
    }

    /**
     * 确认分配
     * @param $param
     * @return bool|int|string
     * @throws \Exception
     * @deprecated  分配中状态被移除 就没有确认接口了
     */
    public function ensureAssignBatch($param)
    {
        // 修改为待确认状态
        return $this->taskModel->where($param['filter'])->save(['assign_status' => 'wait_confirm']);
    }

    /**
     * 获得当前用户的组长部门id
     * @return array
     * @throws \Exception
     */
    public function getAssignUserDepartmentIdList()
    {
        $headmanDepartmentList = $this->getAssignUserDepartmentList();
        return array_column($headmanDepartmentList, 'id');
    }

    /**
     * 获得当前用户的组长部门信息
     * @return array
     * @throws \Exception
     */
    public function getAssignUserDepartmentList()
    {
        $myRemoteUserInfo = teamones_request('teamones-im', 'user/get_my_user_info', [])['data'];
        // 没有部门信息 没办法看到待确认的任务
        if (
            empty($myRemoteUserInfo['role_info'])
            ||
            !in_array(TaskService::HEADMAN_CODE, array_column($myRemoteUserInfo['role_info'], 'code'))) {
            return [];
        }
        return $myRemoteUserInfo['department_info'];
    }

    /**
     * 获得部门管理人员部门信息  （组长，环节制片）
     * @return array
     */
    public function getDepartmentManagerDepartmentList()
    {
        $myRemoteUserInfo = teamones_request('teamones-im', 'user/get_my_user_info', [])['data'];
        if (empty($myRemoteUserInfo['role_info'])) {
            return [];
        }
        $myRoleCode = array_column($myRemoteUserInfo['role_info'], 'code');
        if (!array_intersect([TaskService::HEADMAN_CODE, TaskService::STEP_PRODUCER_CODE], $myRoleCode)) {
            return [];
        }
        // 没有部门信息 没办法看到待确认的任务
        return $myRemoteUserInfo['department_info'];
    }

    /**
     * 等待确认的资产任务列表
     * @param $param
     * @param int $userId
     * @param int $tenantId
     * @return array
     * @throws \Exception
     */
    public function waitConfirmAssetTaskList($param, int $userId, int $tenantId)
    {
        // 没有组长部门id 那么就无法看到待确认的任务信息
        $headmanDepartmentIdList = $this->getAssignUserDepartmentIdList();
        if (empty($headmanDepartmentIdList)) {
            return StepService::formatStepCategoryPlanData([], []);
        }
        $moduleCodes = OptionsService::getInstance()->getOptionsConfigItemData('merge_module_config', 'asset');
        $moduleCodes = explode(',', $moduleCodes);
        $filter = $param['filter'];

        $gradeSearch = $filter['grade'] ?? [];
        $moduleSearchList = $filter['module_code'] ?? [];
        $categoryNameSearch = $filter['category_name'] ?? [];
        $entityNameSearch = $filter['entity_name'] ?? [];
        unset($filter['grade'], $filter['module_code'], $filter['category_name'], $filter['entity_name']);
        $order = $params['order'] ?? 'task.assignee_at DESC,task.id ASC';
        $moduleIdList = [];
        if (!empty($moduleSearchList)) {
            $moduleCodes = array_filter($moduleCodes, function ($item) use ($moduleSearchList) {
                return in_array($item, $moduleSearchList);
            });
        }
        foreach ($moduleCodes as $moduleCode) {
            $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
        }
        $filter['entity.tenant_id'] = $tenantId;
        // 兼容分配到人的数据
        $filter[] = [
            '_logic' => 'or',
            'task.assignee' => $userId,
            'task.department_id' => ['IN', $headmanDepartmentIdList]
        ];
        $filter['entity.module_id'] = ['IN', implode(',', $moduleIdList)];
        $filter['task.assign_status'] = 'wait_confirm';
        $filter['task.step_type'] = 'step';

        if (!empty($categoryNameSearch)) {
            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_category')))"] = $categoryNameSearch;
        }
        if (!empty($gradeSearch)) {
            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',m.code,'_grade')))"] = $gradeSearch;
        }
        if (!empty($entityNameSearch)) {
            $filter['entity.name'] = $entityNameSearch;
        }

        $fields = [
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_grade'))) as grade",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_category'))) as category",
            'entity.initial_episode_code as initial_episode_code', 'entity.showings_number', 'entity.name',
            'task.name as task_name', 'task.plan_start_time as start_time', 'task.plan_end_time as end_time', 'task.id', 'task.department_id',
        ];
        $tasks = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->field(implode(',', $fields))
            ->order($order)
            ->select();
        $projectData = [];
        if (count($tasks) > 0) {
            $projectData['start_time'] = min(array_column($tasks, 'start_time'));
            $projectData['end_time'] = max(array_column($tasks, 'end_time'));
            foreach ($tasks as &$task) {
                StepService::formatGanttItem($task, 'startnoearlierthan');
            }
        }

        return StepService::formatStepCategoryPlanData($tasks, $projectData);
    }

    /**
     * 待确认的镜头任务列表
     * @param $param
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function waitConfirmShotTaskList($param, $userId, $tenantId)
    {
        $headmanDepartmentIdList = $this->getAssignUserDepartmentIdList();
        // 没有组长部门id 那么就无法看到待确认的任务信息
        if (empty($headmanDepartmentIdList)) {
            return StepService::formatStepCategoryPlanData([], []);
        }
        $moduleCodes = OptionsService::getInstance()->getOptionsConfigItemData('merge_module_config', 'shot');
        $moduleCodes = explode(',', $moduleCodes);
        $filter = $param['filter'];
        $moduleSearchList = $filter['module_code'] ?? [];
        $entityNameSearch = $filter['entity_name'] ?? [];
        unset($filter['grade'], $filter['module_code'], $filter['category_name'], $filter['entity_name']);
        $order = $params['order'] ?? 'task.assignee_at DESC,task.id ASC';
        $moduleIdList = [];
        if (!empty($moduleSearchList)) {
            $moduleCodes = array_filter($moduleCodes, function ($item) use ($moduleSearchList) {
                return in_array($item, $moduleSearchList);
            });
        }
        foreach ($moduleCodes as $moduleCode) {
            $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
        }
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
        $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];
        $filter['entity.tenant_id'] = $tenantId;
        // 兼容分配到人的数据
        $filter[] = [
            '_logic' => 'or',
            'task.assignee' => $userId,
            'task.department_id' => ['IN', $headmanDepartmentIdList],
        ];

        $filter['entity.module_id'] = ['IN', implode(',', $moduleIdList)];
        $filter['task.assign_status'] = 'wait_confirm';
        $filter['task.step_type'] = 'step';

        if (!empty($entityNameSearch)) {
            $filter['entity.name'] = $entityNameSearch;
        }

        $fields = [
            "if(entity.module_id = $shotModuleId, entity.name, '') as shot_name",
            "if(entity.module_id = $sequenceModuleId, entity.name, '') as sequence_name",
            'entity.initial_episode_code as initial_episode_code', 'entity.showings_number', 'entity.name', 'entity.entity_id', 'entity.module_id',
            'task.name as task_name', 'task.plan_start_time as start_time', 'task.plan_end_time as end_time', 'task.id', 'task.department_id',
        ];
        $tasks = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->field(implode(',', $fields))
            ->order($order)
            ->select();
        $projectData = [];
        if (count($tasks) > 0) {
            // 查询镜头的序列信息
            $sequenceIdList = array_column(array_filter($tasks, function ($task) use ($shotModuleId) {
                return $task['module_id'] === $shotModuleId;
            }), 'entity_id');
            $sequenceList = [];
            if (count($sequenceIdList) > 0) {
                $sequenceList = $this->entityModel->where(['id' => ['IN', implode(',', $sequenceIdList)]])->field('id,name')->select();
                $sequenceList = array_column($sequenceList, null, 'id');
            }
            $projectData['start_time'] = min(array_column($tasks, 'start_time'));
            $projectData['end_time'] = max(array_column($tasks, 'end_time'));
            foreach ($tasks as &$task) {
                if ($task['module_id'] === $shotModuleId && isset($sequenceList[$task['entity_id']])) {
                    $task['sequence_name'] = $sequenceList[$task['entity_id']]['name'];
                }
                StepService::formatGanttItem($task, 'startnoearlierthan');
            }
        }

        return StepService::formatStepCategoryPlanData($tasks, $projectData);
    }

    /**
     * 获取组员以分配的任务列表
     * @param $param
     * @param $unionID
     * @return array
     * @throws \Exception
     */
    public function getArtistTaskList($param, $unionID)
    {
        $taskIdList = $param['filter']['task_id'];
        $requestData = [
            "user_id" => $unionID
        ];
        $data = teamones_request('teamones-im', 'user/get_other_user_info', $requestData)['data'];
        if (empty($data['department_info'])) {
            throw new LogicException("user not found", ErrorCode::USER_NOT_FOUND);
        }
        $filter = [
            'role_code' => implode(',', [TaskService::ARTILIST_CODE, TaskService::HEADMAN_CODE]),
            'department.id' => ['IN', array_column($data['department_info'], 'id')],
        ];
        $artistUserList = UserService::getInstance()->getUserListByRole($filter);
        $taskList = $this->taskModel->where(['id' => ['IN', implode(',', $taskIdList)]])
            ->field("id,name,code,entity_id,plan_start_time as start_time,plan_end_time as end_time,is_assignee,assignee_producer,assignee_at,executor_tenant_id,assignee,step_type,step_id,step_category_id,IF(executor_tenant_id <> tenant_id and executor_tenant_id <> 0, 'yes', 'no') as is_outside,task.executor as executor,assign_status")
            ->select();
        $entityIdList = array_column($taskList, 'entity_id');
        $entityIdList = array_unique($entityIdList);
        $entityMap = model(EntityModel::class)
            ->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,name,code,module_id,json')
            ->select();
        $entityMap = array_column($entityMap, null, 'id');
        $moduleIdList = array_column($entityMap, 'module_id');
        $moduleIdList = array_unique($moduleIdList);
        $stepCategoryList = model(StepCategoryModel::class)
            ->where([
                'entity_id' => ['IN', implode(',', $moduleIdList)],
                'parent_id' => 0,
            ])
            ->field('name,entity_id')->select();
        $stepCategoryList = array_column($stepCategoryList, 'name', 'entity_id');
        foreach ($entityMap as &$entity) {
            $entity['step_category_name'] = $stepCategoryList[$entity['module_id']] ?? '';
            $entity = array_merge($entity, json_decode($entity['json'], true));
            unset($entity['json']);
        }
        foreach ($artistUserList as &$user) {
            $userId = $user['id'];
            $user['children'] = array_values(array_filter($taskList, function ($item) use ($userId) {
                return $item['executor'] == $userId;
            }));
            StepService::formatGanttItem($user);
        }
        $projectData = [];
        if (count($taskList)) {
            $projectData['start_time'] = min(array_column($taskList, 'start_time'));
            $projectData['end_time'] = max(array_column($taskList, 'end_time'));
        }
        return StepService::formatStepCategoryPlanData($artistUserList, $projectData);
    }

    /**
     * 确认的镜头任务
     * @param $data
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function acceptedShotTaskList($data, $tenantId)
    {
        $res = [
            'total' => 0,
            'rows' => [],
        ];

        $entityModuleList = $data['module_code'];
        $optionsService = OptionsService::getInstance();
        $moduleCodes = $optionsService->getOptionsConfigItemData('merge_module_config', $entityModuleList);
        $moduleCodeStepCategory = $optionsService->getOptionsConfigItemData('assign_task_list_step_category_filter_config', $entityModuleList, 'shot');
        $headmanDepartmentList = $this->getAssignUserDepartmentList();
        if (empty($headmanDepartmentList)) {
            return $res;
        }
        $headmanDepartmentIdList = [];
        $headmanDepartmentListMap = [];
        foreach ($headmanDepartmentList as $headmanDepartmentListItem) {
            $headmanDepartmentIdList[] = $headmanDepartmentListItem['id'];
            $headmanDepartmentListMap[$headmanDepartmentListItem['id']] = $headmanDepartmentListItem;
        }

        $moduleCodes = explode(',', $moduleCodes);
        $param = $data['param'];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $filter = $param['filter'];
        $moduleSearchList = $filter['module_code'] ?? [];
        $entityNameSearch = $filter['entity_name'] ?? [];
        unset($filter['grade'], $filter['module_code'], $filter['category_name'], $filter['entity_name']);
        $filter = parse_filter_to_tp_where($filter, 'entity');
        $order = $param['order'] ?? 'task.assignee_at DESC,task.id ASC';
        $moduleIdList = [];
        if (!empty($moduleSearchList)) {
            $moduleCodes = array_filter($moduleCodes, function ($item) use ($moduleSearchList) {
                return in_array($item, $moduleSearchList);
            });
        }
        foreach ($moduleCodes as $moduleCode) {
            $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
        }
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
//        $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];
        $filter['entity.tenant_id'] = $tenantId;

        $filter['task.department_id'] = ['IN', $headmanDepartmentIdList];
//        $filter['task.assignee'] = $userId;
        if (!empty($moduleCodeStepCategory)) {
            $filter['task.step_category_id'] = ['IN', $moduleCodeStepCategory];
        }

        $filter['task.step_type'] = 'step';
        $filter['entity.module_id'] = ['IN', implode(',', $moduleIdList)];
        $filter['task.assign_status'] = ['IN', [TaskModel::ASSIGN_STATUS_EXECUTOR]];

        if (!empty($entityNameSearch)) {
            $filter['entity.name'] = $entityNameSearch;
        }
        $statusData = array_column(model(StatusModel::class)->select(), 'id', 'code');
        $filter["json_extract(entity.json, concat('$.', m.code,'_status'))"] = ['NEQ', $statusData['closed']];

        $fields = [
            'entity.initial_episode_code', 'entity.showings_number', 'entity.name', 'entity.module_id', 'entity.entity_id as entity_entity_id',
            'task.name as task_name', 'task.plan_start_time as start_time', 'task.plan_end_time as end_time', 'task.id',
            'task.entity_id', 'task.is_assignee', 'task.assign_status', 'task.executor', "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status",
            'task.department_id', "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_workload_remain')) as task_workload_remain", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.is_dismantle_task')) as is_dismantle_task",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.dismantle_name')) as dismantle_name"
        ];
        $res['total'] = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->count();

        $res['rows'] = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->field(implode(',', $fields))
            ->order($order)
            ->page($page[0], $page[1])
            ->select();

        $taskIds = array_column($res['rows'], 'id');
        $shotRowsEntityIds = array_column(array_filter($res['rows'], function ($item) use ($shotModuleId) {
            return $item['module_id'] == $shotModuleId;
        }), 'entity_entity_id');

        $sequenceRows = [];
        if (!empty($shotRowsEntityIds)) {
            $sequenceRows = $this->entityModel->where(['id' => ['IN', join(',', $shotRowsEntityIds)]])->field("id,name")->select();
            $sequenceRows = array_column($sequenceRows, 'name', 'id');
        }


        $userIdList = [];
        $taskStatusIdList = [];
        foreach ($res['rows'] as $row) {


            if ($row['executor'] > 0 && !in_array($row['executor'], $userIdList)) {
                $userIdList[] = $row['executor'];
            }
            if ($row['task_status'] > 0 && !in_array($row['task_status'], $taskStatusIdList)) {
                $taskStatusIdList[] = $row['task_status'];
            }
        }
        $userList = [];
        if (count($userIdList)) {
            $userList = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIdList)]])->field('id,name,avatar,union_id')->select();
            $userList = array_column($userList, null, 'id');
        }
        $statusList = [];
        if (count($taskStatusIdList)) {
            $statusList = model(StatusModel::class)->where(['id' => ['IN', implode(',', $taskStatusIdList)]])->field('id,name,code')->select();
            $statusList = array_column($statusList, null, 'id');
        }

        // 获取所有子任务
        $dismantleTaskData = DismantleTaskService::getInstance()->getDismantleTaskIds($taskIds);
        $isDismantleTaskList = array_group_by($dismantleTaskData, 'source_task_id');
        foreach ($res['rows'] as &$row) {
            if ($row['module_id'] == $shotModuleId) {
                $row['sequence_name'] = $sequenceRows[$row['entity_entity_id']] ?? '';
            } else {
                $row['sequence_name'] = '';
            }

            $row['executor_user'] = $userList[$row['executor']] ?? null;
            if (!empty($row['executor_user'])) {
                $row['executor_union_id'] = $row['executor_user']['union_id'];
            }
            $row['task_status'] = $statusList[$row['task_status']] ?? null;

            //部门信息补充
            if (array_key_exists($row['department_id'], $headmanDepartmentListMap)) {
                $row['department_info'] = $headmanDepartmentListMap[$row['department_id']];
            }
            if (is_null($row['task_workload_remain']) || $row['task_workload_remain'] === '') {
                $row['task_workload_remain'] = 100;
            }
            $row['dismantle_task_ids'] = $isDismantleTaskList[$row['id']] ?? [];
        }

        return $res;
    }


    /**
     * 确认的资产任务
     * @param $data
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function acceptedAssetTaskList($data, $tenantId)
    {
        //todo 待优化 接口慢 sql 查询慢

        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $optionsService = OptionsService::getInstance();
        $entityModuleList = $data['module_code'] ?? "";
        if (!empty($entityModuleList)) {
            $moduleCodes = $optionsService->getOptionsConfigItemData('merge_module_config', $entityModuleList, "");
            $moduleCodeStepCategory = $optionsService->getOptionsConfigItemData('assign_task_list_step_category_filter_config', $entityModuleList, 'shot');
        } else {
            $moduleCodes = $optionsService->getOptionsData('merge_module_config');
            $moduleCodes = array_values($moduleCodes);
            $moduleCodes = join(',', $moduleCodes);
        }

        $moduleCodes = array_unique(explode(',', $moduleCodes));

        //获取自己的部门
        $headmanDepartmentList = $this->getAssignUserDepartmentList();
        if (empty($headmanDepartmentList)) {
            return $res;
        }

        $headmanDepartmentIdList = [];
        $headmanDepartmentListMap = [];
        foreach ($headmanDepartmentList as $headmanDepartmentListItem) {
            $headmanDepartmentIdList[] = $headmanDepartmentListItem['id'];
            $headmanDepartmentListMap[$headmanDepartmentListItem['id']] = $headmanDepartmentListItem;
        }
        $statusData = array_column(model(StatusModel::class)->select(), 'id', 'code');

        $param = $data['param'] ?? [];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $filter = $param['filter'] ?? [];
        $gradeSearch = $filter['grade'] ?? [];
        $moduleSearchList = $filter['module_code'] ?? [];
        $categoryNameSearch = $filter['category_name'] ?? [];
        $entityNameSearch = $filter['entity_name'] ?? [];
        unset($filter['grade'], $filter['module_code'], $filter['category_name'], $filter['entity_name']);
        $filter = parse_filter_to_tp_where($filter, 'entity');
        $order = $param['order'] ?? 'task.assignee_at DESC,task.id ASC';
        $moduleIdList = [];
        if (!empty($moduleSearchList)) {
            $moduleCodes = array_filter($moduleCodes, function ($item) use ($moduleSearchList) {
                return in_array($item, $moduleSearchList);
            });
        }
        foreach ($moduleCodes as $moduleCode) {
            $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
        }

        $filter['task.step_type'] = 'step';
        $filter['entity.tenant_id'] = $tenantId;
        $filter['task.department_id'] = ['IN', $headmanDepartmentIdList];
        if (!empty($moduleCodeStepCategory)) {
            $filter['task.step_category_id'] = ['IN', $moduleCodeStepCategory];
        }
        if (!empty($moduleIdList)) {
            $filter['entity.module_id'] = ['IN', implode(',', $moduleIdList)];
        }

        $filter['task.assign_status'] = ['IN', [TaskModel::ASSIGN_STATUS_EXECUTOR]];

        if (!empty($categoryNameSearch)) {
            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_category')))"] = $categoryNameSearch;
        }
        if (!empty($gradeSearch)) {
            $filter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',m.code,'_grade')))"] = $gradeSearch;
        }
        if (!empty($entityNameSearch)) {
            $filter['entity.name'] = $entityNameSearch;
        }
        // 已取消的不展示
        $filter["json_extract(entity.json, concat('$.', m.code,'_status'))"] = ['NEQ', $statusData['closed']];

        $fields = [
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_grade'))) as grade",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', m.code, '_category'))) as category",
            'entity.initial_episode_code', 'entity.showings_number', 'entity.name', 'entity.entity_id as entity_entity_id',
            'task.name as task_name', 'task.plan_start_time as start_time', 'task.plan_end_time as end_time', 'task.id', 'task.is_assignee', 'task.assign_status', 'task.executor',
            'task.entity_id', "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status", 'task.department_id', 'task.project_id',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_workload_remain')) as task_workload_remain",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.is_dismantle_task')) AS is_dismantle_task",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.dismantle_name')) AS dismantle_name,JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.parent_task_id')) AS parent_task_id"
        ];
        $res['total'] = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->join("status on status.id = json_unquote(json_extract(task.json,'$.task_status'))", 'LEFT')
            ->where($filter)
            ->count();

        $res['rows'] = $this->entityModel
            ->join('module m on m.id = entity.module_id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->field(implode(',', $fields))
            ->page($page[0], $page[1])
            ->order($order)
            ->select();


        $parentEntityIds = [];
        foreach ($res['rows'] as $item) {
            if ($item['entity_entity_id'] > 0) {
                $parentEntityIds[$item['entity_entity_id']] = $item['entity_entity_id'];
            }
        }

        $parentRows = [];
        if (!empty($parentEntityIds)) {
            $parentRows = $this->entityModel
                ->join("module on module.id = entity.module_id")
                ->where(['entity.id' => ['IN', join(',', $parentEntityIds)]])->field("entity.id,entity.name,module.code as module_code")->select();
            $parentRows = array_column($parentRows, null, 'id');
        }

        $userIdList = [];
        $taskStatusIdList = [];
        $projectIds = [];
        foreach ($res['rows'] as $row) {
            if ($row['executor'] > 0 && !in_array($row['executor'], $userIdList)) {
                $userIdList[] = $row['executor'];
            }
            if ($row['task_status'] > 0 && !in_array($row['task_status'], $taskStatusIdList)) {
                $taskStatusIdList[] = $row['task_status'];
            }
            $projectIds[$row['project_id']] = $row['project_id'];
        }

        $projectList = [];
        if (!empty($projectIds)) {
            $projectList = model(ProjectModel::class)->where(['id' => ['IN', $projectIds]])->field("id,name")->select();
            $projectList = array_column($projectList, null, "id");
        }

        $userList = [];
        if (!empty($userIdList)) {
            $userList = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIdList)]])->field('id,name,avatar,union_id')->select();
            $userList = array_column($userList, null, 'id');
        }
        $statusList = [];
        if (!empty($taskStatusIdList)) {
            $statusList = model(StatusModel::class)->where(['id' => ['IN', implode(',', $taskStatusIdList)]])->field('id,name,code')->select();
            $statusList = array_column($statusList, null, 'id');
        }

        // 获取拆分任务下所有的子任务id
        $dismantleTaskIds = [];
        if (!empty($res['rows'])) {
            $taskIds = array_column($res['rows'], 'id');
            $dismantleTaskList = DismantleTaskService::getInstance()->getDismantleTaskIds($taskIds);
            $dismantleTaskIds = array_group_by($dismantleTaskList, 'source_task_id');
        }

        foreach ($res['rows'] as &$row) {
            if (isset($parentRows[$row['entity_entity_id']])) {
                $parentModuleCode = $parentRows[$row['entity_entity_id']]['module_code'];
                $row["{$parentModuleCode}_name"] = $parentRows[$row['entity_entity_id']]['name'];
            }

            $row['executor_user'] = $userList[$row['executor']] ?? null;
            if (!empty($row['executor_user'])) {
                $row['executor_union_id'] = $row['executor_user']['union_id'];
            }
            $row['task_status'] = $statusList[$row['task_status']] ?? null;
            if (isset($projectList[$row['project_id']])) {
                $row['project'] = $projectList[$row['project_id']];
            }
            //部门信息补充
            if (array_key_exists($row['department_id'], $headmanDepartmentListMap)) {
                $row['department_info'] = $headmanDepartmentListMap[$row['department_id']];
            }
            if (is_null($row['task_workload_remain']) || $row['task_workload_remain'] === '') {
                $row['task_workload_remain'] = 100;
            }
            $row['dismantle_task_ids'] = $dismantleTaskIds[$row['id']] ?? [];
        }

        return $res;
    }

    /**
     * 操作待接收的任务
     * @param $param
     * @param $userId
     * @return bool|float|int|string
     * @throws \Exception
     */
    public function handleWaitConfirmTask($param, $userId)
    {
        $updateData = [];
        if ($param['op'] === 'deny') {
            $updateData['assign_status'] = 'rejected';

            // 组长拒绝任务，更新增加未处理数据记录
            Client::send('count_pending', build_queue_data_with_xu([
                'type' => 'assign_task',
                'mode' => 'add_by_filter',
                'data' => $param['filter']
            ]));

        } else if ($param['op'] === 'accept') {
            // 接受任务的时候 写入任务分配组长 为当前接收人
            $updateData['assign_status'] = 'department';
            $updateData['assignee'] = $userId;
        } else {
            throw new LogicException("unsupported operation", ErrorCode::UNSUPPORTED_OPERATION);
        }

        return $this->taskModel->where($param['filter'])->save($updateData);
    }

    /**
     * 查询分配中任务的的供应商树
     * @param $param
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function getAssigningSupplierTaskTree($param, $tenantId)
    {
        $queryFields = [
            "task.id",
            "entity.name as entity_name",
            "task.name",
            "task.code",
            "task.man_hour",
            "task.executor_type",
            "task.entity_id",
            "task.plan_start_time as start_time",
            "task.plan_end_time as end_time",
            "task.is_assignee",
            "task.assignee_producer",
            "task.assignee_at",
            "task.executor_tenant_id",
            "task.assignee",
            "task.step_type",
            "task.step_id",
            "task.step_category_id",
            "task.episode_id",
            "IF(task.executor_tenant_id <> task.tenant_id and task.executor_tenant_id <> 0,'yes','no') as is_outside",
            "task.assign_status",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.tenant_group_id')) as tenant_group_id",
        ];
        $taskIdList = $param['filter']['task_id'];
        $taskList = $this->taskModel->where(['task.id' => ['IN', implode(',', $taskIdList)], 'task.executor_tenant_id' => ['NOT IN', "$tenantId,0"]])
            ->join('entity ON entity.id = task.entity_id')
            ->field($queryFields)
            ->select();
        $res = [
            'success' => [],
            'error' => [],
            'outside' => [],
        ];
        if (empty($taskList)) {
            return $res;
        }
        $outsideExecutorTenantIdList = [];
        $entityIdList = [];
        $tenantGroupIds = [];
        $tenantGroupSupplierRow = [];
        $stepCategoryIdList = [];
        $stepIdList = [];
        $planFilter = [];
        foreach ($taskList as $taskItem) {
            $outsideExecutorTenantIdList[$taskItem['executor_tenant_id']] = $taskItem['executor_tenant_id'];
            $key = 'executor_type:' . $taskItem['executor_type'] . ';executor_tenant:' . $taskItem['executor_tenant_id'] . ';step_category:' . $taskItem['step_category_id'];
            if ($taskItem["executor_type"] == "tenant_group") {
                $key .= ";tenant_group:" . $taskItem["tenant_group_id"];
            }
            if (!isset($tenantGroupSupplierRow[$key])) {
                $tenantGroupSupplierRow[$key] = [
                    'executor_type' => $taskItem['executor_type'],
                    'executor_tenant_id' => $taskItem['executor_tenant_id'],
                    'step_category_id' => $taskItem['step_category_id'],
                ];
                if ($taskItem['executor_type'] == 'tenant_group') {
                    $tenantGroupSupplierRow[$key]['tenant_group_id'] = $taskItem['tenant_group_id'];
                }
            }
            $taskItem['plan_group_id'] = 'episode:' . $taskItem['episode_id'] . ';step_category:' . $taskItem['step_category_id'];
            $planFilter[$taskItem['plan_group_id']] = [
                'r_step_category.id' => $taskItem['step_category_id'],
                'p.entity_id' => $taskItem['episode_id'],
            ];
            $entityIdList[$taskItem["entity_id"]] = $taskItem["entity_id"];
            $stepIdList[$taskItem['step_id']] = $taskItem['step_id'];
            $tenantGroupIds[] = $taskItem['tenant_group_id'];
            $tenantGroupSupplierRow[$key]['children'][] = $taskItem;
            $stepCategoryIdList[$taskItem['step_category_id']] = $taskItem['step_category_id'];
        }
        $tenantGroupIds = array_unique($tenantGroupIds);

        // 环节计划完成时间查询
        $filterDataFilter = array_values($planFilter);
        $filterDataFilter['_logic'] = 'OR';
        $planFilter = [
            $filterDataFilter,
            'p.scope' => PlanModel::SCOPE_SINGLE,
            'p.type' => PlanModel::TYPE_STEP_CATEGORY,
            'r_step_category.link_module_code' => 'step_category',
        ];
        $planFields = [
            "concat('episode:',p.entity_id,';step_category:', r_step_category.id) plan_group_id",
            'p.end_time',
            'p.start_time',
        ];
        $planList = model(PlanModel::class)->alias('p')
            ->join('task_relation_tag r_step_category ON r_step_category.task_id = p.task_id')
            ->where($planFilter)
            ->field($planFields)
            ->select();
        $planList = array_column($planList, 'end_time', 'plan_group_id');

        $param = [
            'param' => [
                'filter' => [
                    'supplier.type' => 'outside',
                    'supplier.from_tenant_id' => ['-in', implode(',', $outsideExecutorTenantIdList)],
                    'supplier.tenant_id' => $tenantId,
                ]
            ]
        ];
        //查询供应商
        $supplierList = teamones_request('teamones-im', 'supplier/select', $param);
        $supplierList = array_column($supplierList['data']['rows'], null, 'from_tenant_id');
        if (empty($supplierList)) {
            return $res;
        }
        //查询集团
        $tenantGroupList = [];
        if (!empty($tenantGroupIds)) {
            $requestData = [
                "param" => [
                    "filter" => [
                        "id" => ["-in", join(",", $tenantGroupIds)],
                    ],
                ],
            ];
            $tenantGroupList = teamones_request('teamones-im', 'tenant_group/select', $requestData);
            $tenantGroupList = $tenantGroupList["data"]["rows"] ?? [];
            $tenantGroupList = array_column($tenantGroupList, null, 'id');
        }

        //判定供应商是否用过saas系统,没用过需要再加入saas user 中
        $insideUserIdList = [];
        foreach ($supplierList as $supplierListItem) {
            $insideUserIdList[] = $supplierListItem["user_id"];
        }
        $insideUserIdList = array_unique($insideUserIdList);
        $userData = UserService::getInstance()->getUsersByUnionIds($insideUserIdList, 'id,union_id');
        if (empty($userData)) {
            throw new LogicException("user info not found", ErrorCode::USER_INFO_NOT_FOUND);
        }
        $entityMap = model(EntityModel::class)
            ->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,initial_episode_code,showings_number,name,code,module_id,json')
            ->select();
        $entityMap = array_column($entityMap, null, 'id');
        $moduleIdList = array_column($entityMap, 'module_id', 'module_id');
        $stepCategoryList = model(StepCategoryModel::class)
            ->where([
                'entity_id' => ['IN', implode(',', $moduleIdList)],
                'parent_id' => 0,
            ])
            ->field('name,entity_id')
            ->select();
        $stepCategoryList = array_column($stepCategoryList, 'name', 'entity_id');

        $taskStepCategoryList = model(StepCategoryModel::class)
            ->where(['id' => ['IN', implode(',', $stepCategoryIdList)]])
            ->field('id,name,code,color')
            ->select();
        $taskStepCategoryList = array_column($taskStepCategoryList, null, 'id');
        $stepList = model(StepModel::class)->where(['id' => ['IN', implode(',', $stepIdList)]])->field('id,name,color,code')->select();
        $stepList = array_column($stepList, null, 'id');
        foreach ($entityMap as &$entity) {
            $entity['step_category_name'] = $stepCategoryList[$entity['module_id']] ?? '';
            $entity = array_merge($entity, json_decode($entity['json'], true));
            unset($entity['json']);
        }
        foreach ($tenantGroupSupplierRow as $tenantGroupSupplierKey => $tenantGroupSupplier) {
            if (!isset($supplierList[$tenantGroupSupplier["executor_tenant_id"]])) {
                continue;
            }
            $supplier = $supplierList[$tenantGroupSupplier["executor_tenant_id"]];
            $supplier['group_id'] = $tenantGroupSupplierKey;
            $supplier['name'] = $supplier['user_name'];
            $supplier['step_category'] = $taskStepCategoryList[$tenantGroupSupplier['step_category_id']] ?? null;
            $supplier["executor_type"] = $tenantGroupSupplier["executor_type"];
            $supplier["executor_tenant_id"] = $tenantGroupSupplier["executor_tenant_id"];
            $supplier['children'] = $tenantGroupSupplier['children'] ?? [];
            if (empty($supplier['children'])) {
                continue;
            }
            foreach ($supplier['children'] as &$task) {
                $task['entity'] = $entityMap[$task['entity_id']] ?? null;
                $task['step'] = $stepList[$task['step_id']] ?? null;
                $task['step_category'] = $supplier['step_category'];
                $task['step_category_plan_end_time'] = $planList[$task['plan_group_id']] ?? null;
            }
            //如果存在集团填充集团
            if ($tenantGroupSupplier["executor_type"] == "tenant_group" && isset($tenantGroupList[$tenantGroupSupplier["tenant_group_id"]])) {
                $supplier["tenant_group"] = $tenantGroupList[$tenantGroupSupplier["tenant_group_id"]];
            }
            $res['outside'][] = $supplier;
        }
        return $res;
    }

    /**
     * 更新任务排期时间
     * @param $data
     * @return array|bool
     * @throws \Exception
     */
    public function updateTaskScheduleDate($data)
    {

        // @update 2021年11月27日 不限制任务的排期时间
//        $task = $this->taskModel->find($data['id']);
//
//        $entity = $this->entityModel->field('initial_episode_code')->find($task['entity_id']);
//        if (empty($entity)) {
//            throw new Exception("entity not found", ErrorCode::ENTITY_NOT_FOUND);
//        }
//        $episode = $this->entityModel->where(['project_id' => $task['project_id'], 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'], 'code' => $entity['initial_episode_code']])->find();
//        if (empty($episode)) {
//            throw new Exception('episode not found', ErrorCode::MISSING_EPISODE);
//        }
//
//        $ps = strtotime($data['plan_start_time']);
//        $pe = strtotime($data['plan_end_time']);
//        $planService = PlanService::getInstance();
//        // 检查集分类计划
//        $entityStepCategoryPlan = $planService->getPlan(['entity_id' => $episode['id'], 'step_category_id' => $task['step_category_id'], 'type' => 'step_category']);
//        if (!empty($entityStepCategoryPlan)) {
//            if ($pe > $entityStepCategoryPlan['end_time'] || $ps < $entityStepCategoryPlan['start_time']) {
//                throw new Exception("task [{$task['name']}] plan date {$data['plan_start_time']}-{$data['plan_end_time']} must be in " . get_format_date($entityStepCategoryPlan['start_time']) . '~' . get_format_date($entityStepCategoryPlan['end_time']), ErrorCode::TASK_PLAN_TIME_MUST_BE_WITHIN_THE_SPECIFIED_RANGE);
//            }
//        } else {
//            $episodePlan = $planService->getPlan(['entity_id' => $episode['id'], 'project_id' => $episode['project_id'], 'type' => 'episode']);
//            if (!empty($episodePlan)) {
//                if ($pe > $episodePlan['end_time'] || $ps < $episodePlan['start_time']) {
//                    throw new Exception("task [{$task['name']}] plan date {$data['plan_start_time']}-{$data['plan_end_time']} must be in " . get_format_date($episodePlan['start_time']) . '~' . get_format_date($episodePlan['end_time']), ErrorCode::TASK_PLAN_TIME_MUST_BE_WITHIN_THE_SPECIFIED_RANGE);
//                }
//            }
//        }


        // 实体计划检查
//        $entityPlan = $this->planService->getPlan(['entity_id' => $task['entity_id'], 'type' => 'entity']);
//        if (!empty($entityPlan)) {
////            $ps = strtotime($data['plan_start_time']);
//            $pe = strtotime($data['plan_end_time']);
//            if ($pe > $entityPlan['end_time']) {
//                throw new Exception("task [{$task['name']}] plan date {$data['plan_start_time']}-{$data['plan_end_time']} must be before " . get_format_date($entityPlan['end_time']), ErrorCode::TASK_PLAN_TIME_MUST_BE_WITHIN_THE_SPECIFIED_RANGE);
//            }
//        }
        $task = $this->taskModel->field('id,plan_end_time,plan_start_time')->find($data['id']);
        if (!empty($data['plan_end_time'])) {
            $data['plan_end_time'] = date("Y-m-d H:i:s", strtotime('+1 days', strtotime($data['plan_end_time'])) - 1);
        }

        //如有时间计划更改 需要延期检测
        if ((!empty($data['plan_start_time']) && ($task['plan_start_time'] != strtotime($data['plan_start_time']))) ||
            (!empty($data['plan_end_time']) && ($task['plan_end_time'] != strtotime($data['plan_end_time'])))) {
            ReviewRequestService::getInstance()->checkDelay($task['id']);
        }

        $flag = $this->taskModel->modifyItem($data);
        if ($flag === false && $this->taskModel->getErrorCode() !== \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
            throw new LogicException($this->taskModel->getError(), $this->taskModel->getErrorCode());
        }

        //同步外包计划时间
        $this->syncOutSourceTaskPlanTime($data, $task);
        return $flag;
    }

    /**
     * 同步外包计划时间
     * @param $data
     * @param $task
     * @return array|bool
     * @throws \Exception
     */
    public function syncOutSourceTaskPlanTime($data, $task)
    {
        if (empty($task)) {
            return [];
        }

        $outSourceSaveData = [];
        $realTask = OrderService::getInstance()->getOutsideTaskByTaskId($task['id']);

        if (empty($realTask)) {
            return [];
        }
        $outSourceSaveData['id'] = $realTask['id'];

        if (!isset($data['plan_start_time']) && !isset($data['plan_end_time'])) {
            return [];
        }

        if (isset($data['plan_start_time']) && $data['plan_start_time'] != $task['plan_start_time']) {
            $outSourceSaveData['plan_start_time'] = $data['plan_start_time'];
        }
        if (isset($data['plan_end_time']) && $data['plan_end_time'] != $task['plan_end_time']) {
            $outSourceSaveData['plan_end_time'] = $data['plan_end_time'];
        }
        // 任务锁定时检测更新的数据
        TaskLockService::getInstance()->TaskLockedCheckUpdateData($task['id'], $data);
        return model(TaskModel::class)->modifyItem($outSourceSaveData);
    }

    /**
     * 撤回任务
     * @param $filter
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function revokeTaskAssigneeStatus($filter, $tenantId)
    {
        /**
         * 撤回分配的任务
         * 1. 内部分配的任务 只要未被接受 就可以重置分配状态和分配人
         * 2. 外部分配的任务 只要未确认 就可以重置分配状态和执行租户信息一集分配人信息
         */
        $filter['tenant_id'] = $tenantId;
        $taskList = $this->taskModel->where($filter)->field('id,assignee,executor_tenant_id,executor,assign_status')->select();
        $res = [
            'changed_result' => null,
            'can_not_change' => []
        ];
        $updateIdList = [];
        // 检查是否可以撤回
        foreach ($taskList as $taskItem) {

            // 分配中/已拒绝的 可以直接撤回 兼容默认状态 可能历史数据也需要撤回
            // 待确认的内部任务也可以撤回
            if (in_array($taskItem['assign_status'], ['assigning', TaskModel::ASSIGN_STATUS_REJECTED, TaskModel::ASSIGN_STATUS_DEFAULT, TaskModel::ASSIGN_STATUS_WAIT_CONFIRM])
            ) {
                $updateIdList[] = $taskItem['id'];
            } else {
                $res['can_not_change'][] = $taskItem['id'];
            }
        }
        if (count($updateIdList)) {
            $res['changed_result'] = $this->taskModel->where(['id' => ['IN', implode(',', $updateIdList)]])
                ->save([
                    'assign_status' => TaskModel::ASSIGN_STATUS_DEFAULT,
                    'assignee' => 0,
                    'executor_tenant_id' => 0,
                ]);
        }
        return $res;
    }

    /**
     * 分配工序分类筛选条件查询
     * @param $moduleCode
     * @return array|false|mixed|string
     */
    private function getStepCategoryFilter($moduleCode)
    {
        $config = OptionsService::getInstance()->getOptionsConfigItemData('assign_task_list_step_category_filter_config', $moduleCode);
        if (empty($config)) {
            return [];
        }
        return model(StepCategoryModel::class)->where(['id' => ['IN', $config], 'is_deprecate' => 'no'])->select();
    }

    /**
     * 查询待分配统计 (任务分类分组数据 包含计划数据)
     * @param $filter
     * @param $moduleCode
     * @return array|false|mixed|string
     */
    public function getWaitAssignStepCategoryCount($filter, $moduleCode)
    {
        $stepCategoryList = $this->getStepCategoryFilter($moduleCode);
        if (empty($stepCategoryList)) {
            return [];
        }
        $episodeId = $filter['episode_id'] ?? 0;
        $projectId = intval($filter['project_id']);
        $showingsNumber = $filter['showings_number'] ?? '';
        $stepCategoryId = $filter['step_category_id'] ?? [];
        $stepCategoryIds = array_column($stepCategoryList, 'id');

        if (is_array($stepCategoryId) && !empty($stepCategoryId)) {
            $stepCategoryIds = array_filter($stepCategoryIds, function ($item) use ($stepCategoryId) {
                return in_array($item, $stepCategoryId);
            });
        }
        $stepCategoryIds = array_values($stepCategoryIds);

        $episodeShowingsMap = [];
        if (!empty($episodeId)) {
            $episode = model(EntityModel::class)->where(['id' => $episodeId])->field('id,name,code')->find();
            if (!empty($episode)) {
                $tmpEpisodeShowings = [
                    'episode_code' => $episode['code'],
                ];
                if (!empty($showingsNumber)) {
                    $tmpEpisodeShowings['showings_number'] = is_array($showingsNumber) ? $showingsNumber : [$showingsNumber];
                }
                $episodeShowingsMap[] = $tmpEpisodeShowings;
            }
        }
        $requestData = [
            'project_id' => $projectId,
            'step_type' => 'step',
            'base_count_step_category_list' => $stepCategoryIds,
            'assign_status' => ['default', 'rejected'],
            'episode_showings_map' => $episodeShowingsMap,
        ];
        return teamones_request('teamones-olap', 'bi/get_wait_assign_step_category_count', $requestData)['data'];
    }

    /**
     * 查询任务列表
     * @param array|null $param
     * @param $currentTenantId
     * @return array
     * @throws \Exception
     */
    public function getTaskList(?array $param, $currentTenantId)
    {
        $taskIdFilter = $param['filter']['id'];
        $stepCategoryId = $param['filter']['step_category_id'];
        $episodeId = $param['filter']['episode_id'];
        $res = [
            'plan' => null,
            'rows' => [],
        ];
        $planService = PlanService::getInstance();
        $plan = $planService->getPlan(['type' => 'step_category', 'entity_id' => $episodeId, 'step_category_id' => $stepCategoryId]);
        if (empty($plan)) {
            $plan = $planService->getPlan(['type' => 'episode', 'entity_id' => $episodeId]);
            if (empty($plan)) {
                throw new LogicException('episode plan not found', ErrorCode::PLAN_NOT_FOUND);
            }
        }
        $res['plan'] = $plan;

        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $taskFields = [
            'id', 'name', 'code', 'man_hour', 'actual_man_hour', 'entity_id', 'entity_module_id', 'tenant_id',
            'step_category_id', 'step_id', 'plan_start_time', 'plan_end_time', 'start_time', 'end_time', 'department_id',
            'executor', 'executor_tenant_id', 'assignee', 'assignee_producer', 'assign_status', "JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status_id",
            "JSON_UNQUOTE(JSON_EXTRACT(json,'$.is_dismantle_task')) as is_dismantle_task"
        ];
        $entityFields = [
            'id', 'name', 'code', 'initial_episode_code', 'showings_number', 'module_id', 'entity_id', 'entity_module_id', 'json',
        ];
        $taskListFilter = [
            'id' => $taskIdFilter,
        ];
        $taskList = $this->taskModel->where($taskListFilter)->page(...$page)->field($taskFields)->select();

        if (empty($taskList)) {
            return [];
        }
        $insideSupplierUserId = [];
        $outsideSupplierTenantId = [];
        $departmentIdList = [];
        $executorUserIdList = [];
        $entityIdList = [];
        $taskStatusIdMap = [];
        foreach ($taskList as $task) {
            if ($task['assignee'] > 0 && !in_array($task['assignee'], $insideSupplierUserId)) {
                $insideSupplierUserId[] = $task['assignee'];
            }
            if ($task['executor_tenant_id'] > 0 && $task['executor_tenant_id'] != $currentTenantId && !in_array($task['executor_tenant_id'], $outsideSupplierTenantId)) {
                $outsideSupplierTenantId[] = $task['executor_tenant_id'];
            }
            if ($task['department_id'] > 0) {
                $departmentIdList[$task['department_id']] = $task['department_id'];
            }
            if ($task['executor'] > 0) {
                $executorUserIdList[$task['executor']] = $task['executor'];
            }
            $entityIdList[$task['entity_id']] = $task['entity_id'];
            if ($task['task_status_id'] > 0) {
                $taskStatusIdMap[$task['task_status_id']] = $task['task_status_id'];
            }
        }

        // 查询任务实体信息
        $entityList = $this->entityModel->where(['id' => ['IN', $entityIdList]])->field($entityFields)->select();
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
        $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];

        $shotEntityList = [];
        $parentEntityIdList = [];
        foreach ($entityList as &$entity) {
            $jsonFields = json_decode($entity['json'], true);
            unset($entity['json']);
            if (!empty($jsonFields)) {
                $entity = $entity + $jsonFields;
            }
            if ($entity['module_id'] === $shotModuleId && $entity['entity_id'] > 0 && $entity['entity_module_id'] === $sequenceModuleId) {
                $shotEntityList[] = &$entity;
                $parentEntityIdList[] = $entity['entity_id'];
            }
        }
        // 镜头需要查询sequence信息
        if (count($parentEntityIdList) > 0) {
            $sequenceList = $this->entityModel->where(['id' => ['IN', $parentEntityIdList]])->field($entityFields)->select();
            $sequenceList = array_column($sequenceList, null, 'id');
            foreach ($sequenceList as &$sequence) {
                $jsonFields = json_decode($sequence['json'], true);
                unset($sequence['json']);
                if (!empty($jsonFields)) {
                    $sequence = $sequence + $jsonFields;
                }
            }
            foreach ($shotEntityList as &$shot) {
                $shot['sequence'] = $sequenceList[$shot['entity_id']] ?? null;
            }
        }
        // 查询任务供应商信息
        $taskService = TaskService::getInstance();
        $supplierRes = [];
        if (!empty($insideSupplierUserId) || !empty($outsideSupplierTenantId) || !empty($departmentIdList) || !empty($executorUserIdList)) {
            $supplierRes = $taskService->queryTaskSupplierInfo($currentTenantId, $insideSupplierUserId, $outsideSupplierTenantId, $departmentIdList, array_values($executorUserIdList));
        }

        // 任务状态查询
        $statusList = [];
        if (count($taskStatusIdMap) > 0) {
            $statusList = model(StatusModel::class)->where(['id' => ['IN', $taskStatusIdMap]])->field('id,name,code')->select();
            $statusList = array_column($statusList, null, 'id');
        }

        $entityList = array_column($entityList, null, 'id');
        foreach ($taskList as &$task) {
            $task['entity'] = $entityList[$task['entity_id']] ?? null;
            $task['supplier_info'] = null;
            if ($task['executor_tenant_id'] > 0 && $task['executor_tenant_id'] != $task['tenant_id']) {
                $task['supplier_info'] = $supplierRes['outside'][$task['executor_tenant_id']] ?? null;
            } elseif ($task['assignee'] > 0) {
                $task['supplier_info'] = $supplierRes['inside'][$task['assignee']] ?? null;
            }
            $task['executor_user'] = $supplierRes['executor'][$task['executor']] ?? null;
            $task['department'] = $supplierRes['department'][$task['department_id']] ?? null;
            $task['task_status'] = $statusList[$task['task_status_id']] ?? null;
        }
        $res['rows'] = $taskList;
        return $res;
    }

    /**
     * 一键完成
     * @param $dataList
     * @param int $userId
     * @param $tenantId
     * @return array
     * @throws Exception
     */
    public function quickDone($dataList, int $userId, $tenantId)
    {
//        $unionIdList = array_column($dataList, 'union_id', 'union_id');
        $taskIdList = array_column($dataList, 'id', 'id');
        $statusService = StatusService::getInstance();
        $reviewingStatus = $statusService->getOne(['code' => "submitted"], 'id');
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])->field("id,name,json_unquote(json_extract(json,'$.task_status')) as task_status_id")->select();

        // 任务状态检查 如果包含审核中 不允许
        foreach ($taskList as $task) {
            if ($task['task_status_id'] === $reviewingStatus['id']) {
                throw new LogicException($task['name'] . ' is still in reviewing status', ErrorCode::TASK_STILL_IN_REVIEWING_STATUS);
            }
        }

//        $userDataList = [];
//        if (count($unionIdList) > 0) {
//            $userDataList = UserService::getInstance()->getUsersByUnionIds($unionIdList, 'id,name,union_id', true);
//            $userDataList = array_column($userDataList, null, 'union_id');
//        }
        $taskDoneStatus = $statusService->getTaskDoneStatus();
        $commonService = CommonService::instance('task');
        $res = [];
        foreach ($dataList as $data) {
            if (!empty($data['plan_end_time'])) {
                $data['plan_end_time'] = date("Y-m-d H:i:s", strtotime('+1 days', strtotime($data['plan_end_time'])) - 1);
            }
            $data['task_status'] = $taskDoneStatus['id'];
            $data['assignee_producer'] = $userId;
            $data['assign_status'] = 'executor';
            $data['assignee_at'] = time();
            $data['executor_confirmed'] = 'yes';
            $data['executor_tenant_id'] = $tenantId;
            $data['is_assignee'] = 'yes';
            if (isset($data['union_id']) && $data['union_id'] > 0) {
//                $user = $userDataList[$data['union_id']] ?? null;
//                if (empty($user)) {
//                    continue;
//                }
                $data['assignee'] = $data['executor'] = $data['union_id'];
            }
            TaskLockService::getInstance()->TaskLockedCheckUpdateData($data['task']['id'], $data);
            $res[] = $commonService->update($data, 'task', false);
        }
        return $res;
    }

    /**
     * 查询任务列表
     * @param array $filter
     * @param string $fields
     * @param array $page
     * @param mixed $order
     * @return array
     * @throws \Exception
     */
    public function selectTaskList(array $filter, string $fields, array $page, $order)
    {
        $filter = $this->parseMultipleEntityTaskFilter($filter);
        $data = model(EntityModel::class)
            ->join('module on entity.module_id = module.id')
            ->join('task on task.entity_id = entity.id')
            ->where($filter)
            ->field(build_complex_query_fields(explode(',', $fields), '__'))->order($order)->page(...$page)->select();
        return array_map(function ($item) {
            $item = separate_complex_query_data($item, '__');
            foreach ($item as $moduleCode => &$moduleData) {
                CommonService::getFormatAttrData(model(get_module_model_name(Module::$moduleDictData['module_index_by_code'][$moduleCode])), $moduleData);
                if (isset($moduleData['json'])) {
                    foreach ($moduleData['json'] as $jsonField => $jsonValue) {
                        $field = explode('_', $jsonField);
                        if (count($field) > 1) {
                            $jsonField = str_replace_once(array_shift($field) . '_', '', $jsonField);
                        }
                        $moduleData[$jsonField] = $jsonValue;
                    }
                    unset($moduleData['json']);
                }
            }
            return $item;
        }, $data);
    }

    /**
     * 查询订单任务列表
     * @param array $param
     * @return array
     * @throws \Exception
     */
    public function getOrderReassignTaskList(array $param)
    {
        $filter = $param['filter'];
        $projectId = $filter['project_id'];
        $stepCategoryId = $filter['step_category_id']; // 二级分类id
        $secondStepCategory = model(StepCategoryModel::class)->find($stepCategoryId);
        if ($secondStepCategory['parent_id'] > 0) {
            $topStepCategory = model(StepCategoryModel::class)->find($secondStepCategory['parent_id']);
        } else {
            $topStepCategory = $secondStepCategory;
        }
        if ($topStepCategory['entity_id'] <= 0) {
            throw_strack_exception('step_category_id param error', ErrorCode::DATA_INVALID);
        }
        $module = Module::$moduleDictData['module_index_by_id'][$topStepCategory['entity_id']] ?? null;
        if (empty($module)) {
            throw_strack_exception('step_category_id param error', ErrorCode::DATA_INVALID);
        }
        $filter['entity.module_id'] = $module['id'];
        $filter['entity.project_id'] = $projectId;
        $filter['task.is_assignee'] = 'no';
        $filter['task.assign_status'] = ['IN', ['default', 'rejected']];
        $filter['task.step_type'] = 'step';

        unset($filter['project_id'], $filter['step_category_id']);
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $order = $param['order'] ?? ['entity.name ASC'];
        $fields = $param['fields'] ?? 'entity.id,entity.name,task.id,task.name';
        return $this->selectTaskList($filter, $fields, $page, $order);
    }

    /**
     * 分配任务给部门
     * @param int $projectId
     * @param $dataList
     * @param int $userId
     * @return array
     * @throws \Exception
     */
    public function batchSetStepTaskDepartmentId(int $projectId, $dataList, int $userId)
    {
        /**
         *
         * 批量分配任务给部门
         * 1. 通过部门分组 进行批量更新
         * 2. 异步发送分配任务通知
         */
        $errorTaskList = [];
        // 1. 单次限制最多50个任务可以分配
        if (count($dataList) > 50) {
            throw new LogicException('Limit 50 pieces of data.', ErrorCode::Batch_Operate_Limit_50);
        }

        // 1. 获取任务默认状态配置，只有任务为默认状态的 才能继续分配给别人
        $statusService = StatusService::getInstance();
        $notStartStatusList = $statusService->getNotStartedStatus();
        $defaultStatusFilterValue = array_column($notStartStatusList, 'id');

        // 2. 查询能够被分配的任务
        $taskDataIds = array_column($dataList, 'task_id');
        $taskQueryFields = [
            'id', "json_unquote(json_extract(json,'$.task_status')) as task_status_id", "assign_status", 'assignee',
            'executor', 'tenant_id', 'step_category_id', 'episode_id', 'plan_delivery_time'
        ];

        $taskStatusDataList = model(TaskModel::class)
            ->where(['id' => ['IN', $taskDataIds]])
            ->field($taskQueryFields)
            ->select();

        if (empty($taskStatusDataList)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        $episodeIdList = [];
        $stepCategoryIdList = [];
        foreach ($taskStatusDataList as $task) {
            if ($task['episode_id'] > 0) {
                $episodeIdList[$task['episode_id']] = $task['episode_id'];
            }
            $stepCategoryIdList[$task['step_category_id']] = $task['step_category_id'];
        }

        $planTimeMap = PlanService::getInstance()->getStepCategoryPlanEndTime($episodeIdList, $stepCategoryIdList, true);

        $taskDataIndexById = array_column($taskStatusDataList, null, 'id');

        // 更新任务状态
        $successTaskIds = [];
        $assignResult = [];
        $departmentIdList = [];
        foreach ($dataList as $item) {
            if (empty($item['department_id'])) {
                continue;
            }
            $departmentIdList[] = $item['department_id'];
        }
        $dataList = array_group_by($dataList, 'department_id');
        if (!empty($departmentIdList)) {
            // 任务分配 检测部门里是否有组长
            $departmentQueryParam = ['param' => [
                'filter' => [
                    'department.id' => ['-in', implode(',', $departmentIdList)],
                    'role.code' => TaskService::HEADMAN_CODE,
                ],
            ]];
            $hasUserDepartment = teamones_request('teamones-im', 'tenant/check_department_user_exist', $departmentQueryParam)['data'];
            $dontHaveUserDepartment = array_diff($departmentIdList, $hasUserDepartment);
            if (!empty($dontHaveUserDepartment)) {
                // 过滤部门没有设置组长的任务
                foreach ($dataList as $departmentId => $taskItem) {
                    if (in_array($departmentId, $dontHaveUserDepartment)) {
                        unset($dataList[$departmentId]);
                        foreach ($taskItem as $taskId) {
                            $errorTaskList[] = [
                                'task_id' => $taskId['task_id'],
                                'err' => 'department you selected,don`t have headman',
                                'code' => ErrorCode::DEPARTMENT_DONT_HAVE_HEADMAN,
                            ];
                        }
                    }
                }
//                throw new LogicException("department you selected,don`t have headman", ErrorCode::DEPARTMENT_DONT_HAVE_HEADMAN);
            }
        }

        $roleFindRequestData = [
            'param' => [
                'filter' => [
                    'code' => TaskService::ARTILIST_CODE
                ]
            ]
        ];
        $artistRoleData = teamones_request('teamones-im', 'role/find', $roleFindRequestData)['data'];

        // 通过项目id找到项目模板配置的工序
        $showProducerStepCategoryConfigArrayGroup = ProjectService::getInstance()->getProducerStepCategoryConfig($projectId);
        foreach ($showProducerStepCategoryConfigArrayGroup as &$showProducerStepCategoryConfigArrayItem) {
            $showProducerStepCategoryConfigArrayItem['step_category_id_list'] = array_column($showProducerStepCategoryConfigArrayItem, 'id');
            sort($showProducerStepCategoryConfigArrayItem['step_category_id_list']);
            $showProducerStepCategoryConfigArrayItem['step_category_ids'] = implode(',', $showProducerStepCategoryConfigArrayItem['step_category_id_list']);
        }
        unset($showProducerStepCategoryConfigArrayItem);

        // 项目成员数据
        $projectMemberData = [];
        $userIds = [];
        $projectMemberUserIdList = ProjectMemberService::getInstance()->getUserIdList(['project_id' => $projectId, 'type' => ProjectMemberModel::PRODUCTION_TYPE]);
        foreach ($dataList ?? [] as $departmentId => $taskList) {
            foreach ($taskList as $taskItem) {
                if (
                    !empty($taskDataIndexById[$taskItem['task_id']])
                    && in_array($taskDataIndexById[$taskItem['task_id']]['task_status_id'], $defaultStatusFilterValue)
//                    && $taskDataIndexById[$taskItem['task_id']]['executor'] === 0
                ) {
                    $task = $taskDataIndexById[$taskItem['task_id']];
                    $stepCategoryIds = '';
                    $updateData = [
                        'id' => $taskItem['task_id'],
                        'department_id' => $departmentId,
                        'assignee_producer' => $userId,
                        'assignee_at' => time(),
                        'executor_type' => 'inside',
                        'assignee' => 0,
                        'executor' => 0,
                        'executor_tenant_id' => 0,
                        'team' => $taskItem['team'] ?? [],
                    ];
                    // 匹配项目大环节
                    foreach ($showProducerStepCategoryConfigArrayGroup as $showProducerStepCategoryConfigArrayItem) {
                        $stepCategoryId = $taskDataIndexById[$taskItem['task_id']]['step_category_id'];
                        if (in_array($stepCategoryId, $showProducerStepCategoryConfigArrayItem['step_category_id_list'])) {
                            $stepCategoryIds = $showProducerStepCategoryConfigArrayItem['step_category_ids'];
                            break;
                        }
                    }
                    if (!empty($taskItem['assignee']) && $taskItem['assignee'] > 0) {
                        $updateData['assignee'] = $taskItem['assignee'];
                    }
                    if (!empty($taskItem['executor_id']) && $taskItem['executor_id'] > 0) {
                        $updateData = array_merge($updateData, [
                            'executor' => $taskItem['executor_id'],
                            'executor_tenant_id' => $taskDataIndexById[$taskItem['task_id']]['tenant_id'],
                            'is_assignee' => 'yes',
                            'assign_status' => TaskModel::ASSIGN_STATUS_EXECUTOR,
                        ]);
                        // 任务没有计划交付时间 固化计划交付时间
                        if ($task['plan_delivery_time'] <= 0 && !empty($planTimeMap[$task['episode_id']][$task['step_category_id']]['end_time'])) {
                            $updateData['standard_plan_time'] = $updateData['plan_delivery_time'] = $planTimeMap[$task['episode_id']][$task['step_category_id']]['end_time'];
                            $updateData['standard_plan_id'] = $planTimeMap[$task['episode_id']][$task['step_category_id']]['id'];
                        }
                        if (!in_array($taskItem['executor_id'], $projectMemberUserIdList)) {
                            if (!empty($stepCategoryIds)) {
                                $projectMemberData[] = [
                                    'user_id' => $taskItem['executor_id'],
                                    'role_code' => $artistRoleData['code'],
                                    'role_id' => $artistRoleData['id'] ?? 0,
                                    'step_category_ids' => $stepCategoryIds
                                ];
                                $projectMemberUserIdList[] = $taskItem['executor_id'];
                            }
                        }
                        $userIds[] = $taskItem['executor_id'];
                    }
//                    model(TaskModel::class)->save($updateData);
                    CommonService::instance('task')->update($updateData, 'task', false);
                    $successTaskIds[] = $taskItem['task_id'];
                    $assignResult[$departmentId][$stepCategoryIds] = $successTaskIds;
                    Client::send('task_assemble_check', build_queue_data_with_xu(['task_id' => $taskItem['task_id']]));
                }
            }
        }

        // 项目成员添加
        if (!empty($projectMemberData)) {
            $projectService = ProjectService::getInstance();
            $projectService->batchCreateProjectMemberV2($projectId, ProjectMemberModel::PRODUCTION_TYPE, $projectMemberData);
        }

        // 异步队列处理
        if (!empty($assignResult)) {
            try {
                // 发送分配通知
                Client::send('send_assign_task_to_department_notify', build_queue_data_with_xu(['assign_user' => $userId, 'assign_result' => $assignResult]));
                // 添加部门用户到项目成员
                Client::send('add_department_user_to_project_member', build_queue_data_with_xu(['department_step_category_ids_relation' => $assignResult, 'project_id' => $projectId, 'role_code' => TaskService::HEADMAN_CODE]));
            } catch (Throwable $e) {
                trace(format_exception_info($e), 'ERR');
            }
        }
        // 返回修改成功的用户信息
        $userInfo = [];
        if (!empty($userIds)) {
            $userInfo = model(UserModel::class)->field('id,name,avatar')->where(['id' => ['IN', $userIds]])->select();
        }

//        // 发送任务分配通知（新）
//        $hookData = [
//            'type' => 'task_assign_v2',
//            'task_assign_list' => $hookDataTaskAssign,
//            'user_id' => request()->getUserId(),
//            'time' => time(),
//        ];
//        Hook::listen('task_assign', $hookData);

        $res['user_list'] = $userInfo;
        $res['success_list'] = $successTaskIds;
        $res['error_list'] = $errorTaskList;
        return $res;
    }

    /**
     * 获得分配任务通知消息模板id
     * @return int
     */
    public function getSetTaskExecutorMessageTemplateId()
    {
        $templateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'set_task_executor');
        return (int)$templateId;
    }

    /**
     * 任务分配发送异步通知
     * @param int $assignUserId
     * @param array $assignResult
     * @return bool
     * @throws \Exception
     */
    public function sendAssignTaskToDepartmentNotify(int $assignUserId, array $assignResult)
    {
        if (empty($assignResult)) {
            return true;
        }
        $departmentIdList = array_keys($assignResult);
        $users = teamones_request('teamones-im', 'tenant/select_tenant_user', [
            'param' => [
                'filter' => [
                    'department.id' => ['-in', implode(',', $departmentIdList)],
                    'role.code' => TaskService::HEADMAN_CODE
                ],
            ]
        ])['data'];
        $remoteUserData = [];
        foreach ($users as $user) {
            foreach ($user['department_info'] as $department) {
                $remoteUserData[$department['id']][] = $user;
            }
        }

        $assignUser = model(UserModel::class)->find($assignUserId);
        $notifyMessages = [];

        foreach ($assignResult as $departmentId => $taskIdList) {

            $currentHeadmanUserList = $remoteUserData[$departmentId] ?? [];
            if (count($currentHeadmanUserList) === 0) {
                continue;
            }

            $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIdList]])->field('plan_end_time,project_id')->select();
            $subTaskEndTime = array_column($taskList, 'plan_end_time');
            $notifyParam = [
                'first' => "新到了一批任务",
                'remark' => "请及时分配任务",
                'assign_username' => $assignUser['name'],
                'assign_time' => date("Y/m/d H:i"),
            ];
            $notifyParam['task_number'] = count($taskIdList);
            $notifyParam['task_start_time'] = date("Y/m/d H:i", min($subTaskEndTime));
            $notifyParam['task_end_time'] = date("Y/m/d H:i", max($subTaskEndTime));
            foreach ($currentHeadmanUserList as $headmanUser) {
                $notifyMessages[] = [
                    'param' => $notifyParam,
                    'receiver' => $headmanUser['user_id'],
                    'project_id' => $taskList[0]['project_id'],
                ];
            }
        }

        $templateId = $this->getSetTaskExecutorMessageTemplateId();
        foreach ($notifyMessages as $message) {
            Client::send("send_notify_message_v2", build_queue_data_with_xu([
                "param" => $message['param'],
                'receiver' => $message['receiver'],
                'template_id' => $templateId,
                'content_type' => 'card_message',
                'notice_type' => 'message_task',
                'is_popup' => 1,
                'message_key' => 'step_category_task_assignee',
                'project_id' => $message['project_id'],
            ]));
        }
        return true;
    }

    /**
     * 把部门指定角色成员加入项目成员
     * @param array $departmentStepCategoryIdsRelation
     * @param int $projectId
     * @param string $roleCode
     * @throws \Exception
     */
    public function addDepartmentUserToProjectMember(array $departmentStepCategoryIdsRelation, int $projectId, string $roleCode)
    {
        $departmentIdList = array_keys($departmentStepCategoryIdsRelation);
        $role = teamones_request('teamones-im', 'role/find', [
            'param' => [
                'filter' => [
                    'code' => $roleCode
                ]
            ]
        ])['data'];
        $remoteUserData = teamones_request('teamones-im', 'tenant/select_tenant_user', [
            'param' => [
                'filter' => [
                    'department.id' => ['-in', implode(',', $departmentIdList)],
                    'role.code' => $roleCode,
                ],
            ],
        ])['data'];
        $unionIdList = array_column($remoteUserData, 'user_id');
        $userList = UserService::getInstance()->getUsersByUnionIds($unionIdList, 'id,name,union_id', true);
        $userList = array_column($userList, null, 'union_id');
        $remoteUserDataGroupByDepartment = [];
        foreach ($remoteUserData as $remoteUserDatum) {
            foreach ($remoteUserDatum['department_info'] as $department) {
                if (!in_array($department['id'], $departmentIdList)) {
                    continue;
                }
                $remoteUserDataGroupByDepartment[$department['id']][] = $remoteUserDatum;
            }
        }
        $projectMemberUserIdList = ProjectMemberService::getInstance()->getUserIdList(['project_id' => $projectId, 'type' => ProjectMemberModel::PRODUCTION_TYPE]);
        $addData = [];
        foreach ($remoteUserDataGroupByDepartment as $departmentId => $remoteUserList) {
            if (!in_array($departmentId, $departmentIdList)) {
                continue;
            }
            foreach ($departmentStepCategoryIdsRelation[$departmentId] as $stepCategoryIds) {
                foreach ($remoteUserList as $user) {
                    $localUser = $userList[$user['user_id']] ?? null;
                    if (empty($localUser)) {
                        continue;
                    }
                    if (in_array($localUser['id'], $projectMemberUserIdList)) {
                        continue;
                    }
                    $projectMemberUserIdList[] = $localUser['id'];

                    $addData[] = [
                        'project_id' => $projectId,
                        'role_code' => $roleCode,
                        'role_id' => $role['id'] ?? 0,
                        'user_id' => $localUser['id'],
                        'step_category_ids' => $stepCategoryIds,
                        'type' => ProjectMemberModel::PRODUCTION_TYPE,
                        'uuid' => create_uuid(),
                    ];
                }
            }
        }

        if (!empty($addData)) {
            model(ProjectMemberModel::class)->addAll($addData);
        }
    }

    /**
     * 分配任务撤回
     * @param array $filter
     * @param int $userId
     * @return array
     * @throws Throwable
     */
    public function assignWithdraw(array $filter, int $userId)
    {
        $res = [];
        $taskList = $this->taskModel->where($filter)->select();

        // 分拣出外包撤回和内部撤回
        $insideTaskList = [];
        $outsideTaskList = [];
        foreach ($taskList as $task) {
            $task = CommonService::appendJsonFields($task, 'json');
            if (in_array($task['executor_type'], TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP)) {
                $outsideTaskList[] = $task;
            } else {
                $insideTaskList[] = $task;
            }
        }
        if (!empty($insideTaskList)) {
            $res['inside'] = $this->handleInsideTaskAssignWithdraw($insideTaskList, true);
        }
        if (!empty($outsideTaskList)) {
            $res['outside'] = $this->handleOutsideTaskAssignWithdraw($outsideTaskList, $userId);
        }

        $hookData = [
            'task_list' => $taskList,
            'user_id' => request()->getUserId(),
        ];
        Hook::listen('task_assign_withdraw', $hookData);
        return $res;
    }

    /**
     * 查询待分配的任务列表高级版
     * @param $param
     * @param int $userId
     * @param $tenantId
     * @return array
     */
    public function getStepTaskWaitForAssignPrime($param, int $userId, $tenantId): array
    {
        $isAssign = $param['is_assigned'] ?? 'no';
        $filter = $param['filter'] ?? [];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];

        $queryField = [
            "task.id", "task.name", "task.code", "task.type", "task.man_hour", "task.actual_man_hour", "task.plan_start_time",
            "task.plan_end_time", "task.plan_duration", "task.entity_module_id", "task.step_type", "task.step_id", "task.step_category_id",
            "task.project_id", "task.executor", "task.assignee", "task.entity_id", "task.entity_module_id", "task.task_status", "task.pre_task_id",
            "entity.id", "entity.name", "entity.code", "entity.json", "entity.project_id", "entity.initial_episode_code",
            "entity.entity_id", "entity.entity_module_id",
        ];

        $entityQueryField = [
            "entity.id", "entity.name", "entity.code", "entity.json", "entity.project_id", "entity.initial_episode_code",
            "entity.entity_id", "entity.entity_module_id",
        ];

        $filter = array_merge($filter, [
            'task.assignee' => $userId,
            'task.type' => "sub",
            'task.step_type' => "step",
            'task.tenant_id' => $tenantId,
            "_logic" => "AND",
        ]);
        if ($isAssign === 'yes') {
            $filter[] = [
                'task.is_assignee' => 'yes',
                'task.executor' => ['GT', 0],
            ];
        } else {
            $filter[] = [
                'task.is_assignee' => 'no',
                'task.executor' => 0,
            ];
        }

        if (isset($param['filter']['project_id']) && $param['filter']['project_id'] > 0) {
            $filter['task.project_id'] = (int)$param['filter']['project_id'];
        }
        $filter = parse_filter_to_tp_where($filter, 'task');

        $queryField = build_complex_query_fields($queryField, '__');
        $total = model(TaskModel::class)
            ->join('entity ON entity.id = task.entity_id', 'LEFT')
            ->where($filter)
            ->count();
        $tasks = [];
        if ($total > 0) {
            $tasks = model(TaskModel::class)->join('entity ON entity.id = task.entity_id', 'LEFT')
                ->where($filter)->field($queryField)->page($page[0], $page[1])->select();
            $tasks = array_column($tasks, null, 'id');
            // 遍历 提取数据
            $userIds = $entityIds = $filterEpisodes = $statusIds = $stepCategoryIdData = $stepIdData = $projectIds = [];
            foreach ($tasks as &$task) {
                $task = separate_complex_query_data($task, '__');
                if ($task['task']['executor'] > 0) {
                    $userIds[] = $task['task']['executor'];
                }
                if ($task['task']['assignee'] > 0) {
                    $userIds[] = $task['task']['assignee'];
                }
                if (isset($task['entity'])) {
                    $task['entity'] = array_merge($task['entity'], json_decode($task['entity']['json'], true) ?? []);
                    unset($task['entity']['json']);
                }

                $projectId = $task['task']['project_id'];
                $entityIds[] = $task['entity']['entity_id'];
                $projectIds[] = $projectId;
                $filterEpisodes[$projectId][] = $task['entity']['initial_episode_code'];
                if (isset($task['task']['task_status']) && (int)$task['task']['task_status'] > 0) {
                    $statusIds[] = $task['task']['task_status'];
                }
                if (isset($task['task']['step_category_id']) && $task['task']['step_category_id'] > 0) {
                    $stepCategoryIdData[] = $task['task']['step_category_id'];
                }
                if (isset($task['task']['step_id']) && $task['task']['step_id'] > 0) {
                    $stepIdData[] = $task['task']['step_id'];
                }
            }

            // 查询用户信息
            if (count($userIds) > 0) {
                $userIds = array_unique($userIds);
                $users = model(UserModel::class)->where(['id' => ['IN', join(',', $userIds)]])->field("id,name,email")->select();
                $users = array_column($users, null, 'id');
            }
            //查询关联的实体信息
            $entityData = [];
            if (count($entityIds) > 0) {
                $entityIds = array_unique($entityIds);
                $entityData = model(EntityModel::class)->where(['id' => ['IN', implode(',', $entityIds)]])
                    ->field($entityQueryField)->select();
                $entityData = array_map(function ($item) {
                    return CommonService::appendJsonFields($item, 'json');
                }, $entityData);
                $entityData = array_column($entityData, null, 'id');
            }

            // 集查询
            $episodeMap = [];
            if (count($filterEpisodes) > 0) {
                $episodeMap = EpisodeService::getInstance()->getMultipleProjectEpisodes($filterEpisodes);
            }

            //查询项目信息
            $projectData = [];
            if (count($projectIds) > 0) {
                $projectService = ProjectService::getInstance();
                $projectData = $projectService->getProjects(['id' => ['IN', implode(',', $projectIds)]], "id,name,code,belong_project_id");
                $projectData = array_column($projectData, null, 'id');
            }

            //查询状态信息
            $statusData = [];
            if (count($statusIds) > 0) {
                $statusIds = array_unique($statusIds);
                $statusData = model(StatusModel::class)
                    ->where(['id' => ['IN', implode(',', $statusIds)]])
                    ->field("id,name,color,code,correspond,icon")
                    ->select();
                $statusData = array_column($statusData, null, 'id');
            }

            // 查询分类信息
            $stepCategoryMap = [];
            if (count($stepCategoryIdData) > 0) {
                $stepCategoryIdData = array_unique($stepCategoryIdData);
                $stepCategoryMap = model(StepCategoryModel::class)
                    ->where(['id' => ['IN', implode(',', $stepCategoryIdData)]])
                    ->select();
                $stepCategoryMap = array_column($stepCategoryMap, null, 'id');
            }

            // 查询工序信息
            $stepMap = [];
            if (count($stepIdData) > 0) {
                $stepIdData = array_unique($stepIdData);
                $stepMap = model(StepModel::class)
                    ->where(['id' => ['IN', implode(',', $stepIdData)]])
                    ->select();
                $stepMap = array_column($stepMap, null, 'id');
            }

            //填充关联的数据
            foreach ($tasks as &$task) {
                $task['executor'] = $users[$task['task']['executor']] ?? null;
                $task['assignee'] = $users[$task['task']['assignee']] ?? null;
                $task['module'] = Module::$moduleDictData['module_index_by_id'][$task['task']['entity_module_id']] ?? null;
                if (!empty($task['entity'])) {
                    // 当前任务实体的父级实体信息
                    $parentEntityModule = Module::$moduleDictData['module_index_by_id'][$task['entity']['entity_module_id']] ?? null;
                    if (!empty($parentEntityModule)) {
                        $task[$parentEntityModule['code']] = $entityData[$task['entity']['entity_id']] ?? null;
                    }
                    $task['entity_name'] = $task['entity']['name'];
                    if (isset($episodeMap[$task['task']['project_id']][$task['entity']['initial_episode_code']])) {
                        $task['episode'] = $episodeMap[$task['task']['project_id']][$task['entity']['initial_episode_code']];
                    } else {
                        $task['episode'] = null;
                    }
                }

                $task['project'] = $projectData[$task['task']['project_id']] ?? null;
                $task['task_status'] = $statusData[$task['task']['task_status']] ?? null;
                $task['step'] = $stepMap[$task['task']['step_id']] ?? null;
                $task['step_category'] = $stepCategoryMap[$task['task']['step_category_id']] ?? null;
            }
        }

        return ['total' => $total, 'rows' => $tasks];
    }

    /**
     * 修改执行人
     * @param array $data
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function changeExecutor(array $data)
    {
        $task = $this->taskModel->find($data['id']);
        $jsonFields = json_decode($task['json'], true);
        $taskWorkloadRemain = $jsonFields['task_workload_remain'] ?? 100;
        $taskStatus = $jsonFields['task_status'];
        $canChangeStatusCode = OptionsService::getInstance()->getOptionsConfigItemData('task_config', 'change_executor_status_limit', 'feedback,inprogress,remake');
        $statusList = StatusService::getInstance()->getList(['code' => ['in', $canChangeStatusCode]], 'id,code');
        $statusIdList = array_column($statusList, 'id');
        if ($taskWorkloadRemain < $data['done_percent']) {
            throw new LogicException('data invalid,' . "done_percent should not bigger than {$taskWorkloadRemain}", ErrorCode::DATA_INVALID);
        }
        if (!in_array($taskStatus, $statusIdList)) {
            throw new LogicException($taskStatus . ' can`t change executor', ErrorCode::TASK_CANT_CHANGE_EXECUTOR_BY_STATUS_VALIDATE);
        }
        if ($task['executor'] == $data['new_executor_id']) {
            throw new LogicException('data invalid, new executor is same to old executor', ErrorCode::DATA_INVALID);
        }
        $executorRecord = [
            'task_id' => $task['id'],
            'user_id' => $task['executor'],
            'done_percent' => $data['done_percent'],
            'next_user_id' => $data['new_executor_id'],
        ];
        $taskUpdateData = [
            'id' => $task['id'],
            'executor' => $data['new_executor_id'],
            'task_workload_remain' => $taskWorkloadRemain - $executorRecord['done_percent'],
        ];

        //找到当前部门身份
        $userUnionId = model(UserModel::class)->field("union_id")->where(['id' => $data['new_executor_id']])->find();

        $departmentUserList = TenantService::getInstance()->selectDepartmentUserList([
            "param" => [
                "filter" => [
                    "tenant_id" => $task["tenant_id"],
                    "department_id" => $task["department_id"],
                    "user_id" => $userUnionId["union_id"]
                ]
            ]
        ]);

        $departmentUserRoleCode = TaskService::ARTILIST_CODE;
        if (!empty($departmentUserList["rows"][0]["role_info"])) {
            $departmentUserRoleCode = $departmentUserList["rows"][0]["role_info"][0]['code'];
        }

        // 通过项目id找到项目模板配置的工序
        $showProducerStepCategoryConfigArrayGroup = ProjectService::getInstance()->getShowStepCategoryGroup($task['project_id']);
        $stepCategoryIdArr = [];
        if (!empty($showProducerStepCategoryConfigArrayGroup)) {
            foreach ($showProducerStepCategoryConfigArrayGroup as $item) {
                if (in_array($task['step_category_id'], $item['children_ids'])) {
                    $stepCategoryIdArr = $item['children_ids'];
                    sort($stepCategoryIdArr);
                    break;
                }
            }
        }
        $stepCategoryIds = join(',', $stepCategoryIdArr);

        //判定是否添加过组员身份
        $projectMemberData = [
            "project_id" => $task["project_id"],
            "user_id" => $data['new_executor_id'],
            "role_code" => $departmentUserRoleCode,
            'type' => ProjectMemberModel::PRODUCTION_TYPE,
            'step_category_ids' => $stepCategoryIds
        ];
        $projectMember = model(ProjectMemberModel::class)->where($projectMemberData)->find();

        $commonService = CommonService::instance('task');
        $res = [];
        $this->taskModel->startTrans();
        try {
            $taskUpdateData = $commonService->dealUpdateCustomField($taskUpdateData, 'task');
            $res['task'] = $this->taskModel->modifyItem($taskUpdateData);
            if (empty($projectMember)) {

                $roleFindRequestData = ['param' => ['filter' => ['code' => $departmentUserRoleCode]]];
                $artistRoleData = teamones_request('teamones-im', 'role/find', $roleFindRequestData)['data'];

                $projectMemberData['step_category_ids'] = $stepCategoryIds;
                $projectMemberData['type'] = ProjectMemberModel::PRODUCTION_TYPE;
                $projectMemberData['role_id'] = $artistRoleData['id'] ?? 0;

                model(ProjectMemberModel::class)->addItem($projectMemberData);
            }
            $res['task_executor_record'] = model(TaskExecutorRecordModel::class)->addItem($executorRecord);

            // 更换执行人,暂停正在执行的 TimeLog
            $timeLog = model(TimelogModel::class)->findData([
                'filter' => [
                    'module_id' => Module::$moduleDictData['module_index_by_code']['task']['id'],
                    'link_id' => $task['id'],
                    'complete' => 'no',
                ],
                'order' => 'id DESC',
            ]);
            if (!empty($timeLog)) {
                // 暂停当前任务正在执行的timeLog
                $paramStop = [
                    'id' => $timeLog['id'],
                ];
                TimelogService::getInstance()->stopTimer($paramStop);
            }

            $this->taskModel->commit();
            return $res;
        } catch (Throwable $e) {
            $this->taskModel->rollback();
            throw $e;
        }
    }

    /**
     * 查询分配任务弹窗数据
     * @param array $entityIdList
     * @param int $stepCategoryId
     * @return array
     */
    public function getAssignPopTaskTreeByEntityStepCategory(array $entityIdList, int $stepCategoryId)
    {
        $taskFilter = [
            'entity_id' => ['IN', $entityIdList],
            'step_category_id' => $stepCategoryId,
            'assign_status' => ['IN', [TaskModel::ASSIGN_STATUS_DEFAULT]]
        ];
        $taskIdList = model(TaskModel::class)->where($taskFilter)->field('id')->column('id');
        return ShotService::getInstance()->getAssignPopTaskTree($taskIdList);
    }

    /**
     * 处理内部任务撤回
     * @param array $insideTaskList
     * @param bool $needResetTaskStatus
     * @return bool|float|int|string
     * @throws \Exception
     */
    private function handleInsideTaskAssignWithdraw(array $insideTaskList, $needResetTaskStatus = false)
    {
        $ids = implode(',', array_column($insideTaskList, 'id'));
        $updateFilter = [
            'id' => ['IN', $ids]
        ];
        $resetTaskData = [
            'department_id' => 0,
            'assign_status' => TaskModel::ASSIGN_STATUS_DEFAULT,
            'executor_type' => 'default',
            'executor_tenant_id' => 0,
            'assignee' => 0,
            'executor' => 0,
            'is_assignee' => 'no',
        ];

        if ($needResetTaskStatus) {
            $defaultNotStartStatus = StatusService::getInstance()->getTaskDefaultStatus();
            model(TaskModel::class)->execute("update task set json = json_set(json,'$.task_status',{$defaultNotStartStatus['id']}) where id in ($ids)");
        }
        return model(TaskModel::class)->where($updateFilter)->save($resetTaskData);
    }

    /**
     * 处理外包任务撤回
     * @param array $outsideTaskList
     * @param int $userId
     * @return array
     * @throws Throwable
     */
    private function handleOutsideTaskAssignWithdraw(array $outsideTaskList, int $userId)
    {
        /**
         * 1. 修改订单任务列表的状态为已撤回
         * 2. 重置甲方任务的分配状态
         * 3. 重置甲方任务的任务状态
         * 4. 废除乙方任务,删除订单文件上下游传输记录
         * 5. 异步生成订单的上下游文件记录
         */
        $outsideTaskIdList = array_column($outsideTaskList, 'id');
        $orderTaskList = OrderService::getInstance()->getOrderTaskListByTaskIdList($outsideTaskIdList);
        $orderRealTaskIdList = array_filter(array_column($orderTaskList, 'real_task_id'));
        $realTaskList = [];
        if (!empty($orderRealTaskIdList)) {
            $realTaskList = model(TaskModel::class)
                ->where(['id' => ['IN', array_column($orderTaskList, 'real_task_id')]])
                ->select();
        }
        $res = [];
        $notStartedStatus = StatusService::getInstance()->getTaskDefaultStatus();
        model(TaskModel::class)->startTrans();
        try {
            // 修改订单任务列表的状态为已撤回
            if (!empty($orderTaskList)) {
                $res['order_task_list'] = model(OrderTaskListModel::class)->where(['id' => ['IN', array_column($orderTaskList, 'id')]])->save(['status' => 'withdraw']);
            }
            // 重置甲方任务的分配状态
            $res['inside_task_withdraw'] = $this->handleInsideTaskAssignWithdraw($outsideTaskList, true);

            // 重置甲方任务的任务状态
            $this->taskModel->execute("UPDATE task set json=JSON_SET(IFNULL(json,'{}'), '$.task_status',{$notStartedStatus['id']}) where id in (" . implode(',', $outsideTaskIdList) . ")");
            foreach ($outsideTaskIdList as $taskId) {
                Client::send('task_ready_check', build_queue_data_with_xu(['task_id' => $taskId]));
            }
            // 废除乙方任务
            if (!empty($realTaskList)) {
                $res['outside_task_close'] = TaskAbolitionService::getInstance()->taskClose($realTaskList, false, $userId, 'assign_withdraw');
                // 删除订单文件上下游传输记录
                $res['order_task_file_list_remove'] = OrderTaskFileListService::getInstance()->deleteByDownstreamTaskIdList(array_column($realTaskList, 'id'));
            }
            if (!empty($orderTaskList)) {
                $orderIdList = array_column($orderTaskList, 'order_id', 'order_id');
                $hasOrderTaskOrderIdList = model(OrderTaskListModel::class)
                    ->where([
                        'order_id' => ['IN', array_keys($orderIdList)],
                        'status' => OrderTaskListModel::STATUS_DEFAULT,
                    ])
                    ->group('order_id')
                    ->field('order_id')
                    ->column('order_id');

                $cancelOrderIdList = [];
                foreach ($orderIdList as $orderId) {
                    // 检查订单是否还有任务
                    if (!in_array($orderId, $hasOrderTaskOrderIdList)) {
                        $cancelOrderIdList[] = $orderId;
                    } else {
                        // 订单还有任务 重新生成上下游传输记录 避免共享依赖造成数据缺失
                        Client::send('generate_order_task_file_list', build_queue_data_with_xu(['order_id' => $orderId, 'user_id' => $userId]), 3);
                    }
                }
                // 没有订单的任务进入已取消状态
                if (!empty($cancelOrderIdList)) {
                    model(OrderModel::class)->where(['id' => ['IN', $cancelOrderIdList]])->save([
                        'status' => 'cancel',
                    ]);
                }
            }
            model(TaskModel::class)->commit();
        } catch (Throwable $e) {
            model(TaskModel::class)->rollback();
            throw $e;
        }
        return $res;
    }
}
